package club.web51.classroominteractiontoolsadmin.Security;

import club.web51.classroominteractiontoolsadmin.DO.SystemDO.OpreationLog;
import club.web51.classroominteractiontoolsadmin.Enums.MsgEnum;
import club.web51.classroominteractiontoolsadmin.Exceptions.ErrDataException;
import club.web51.classroominteractiontoolsadmin.Repositories.OpreationLogRepository;
import club.web51.classroominteractiontoolsadmin.Repositories.UserRepository;
import club.web51.classroominteractiontoolsadmin.Services.UserService;
import club.web51.classroominteractiontoolsadmin.Utils.MsgUtil;
import club.web51.classroominteractiontoolsadmin.Utils.ObjectUtil;
import club.web51.classroominteractiontoolsadmin.VO.TokenVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Objects;

/**
 * @Classname TokenAop
 * @Description TODO 全局Token切面拦截处理（注解）
 * @Date 2020/11/20 0:01
 * @Created by HanYuHao
 */
@Component
@Aspect
@Slf4j
public class TokenAop {
    @Resource
    UserService userService;
    @Resource
    UserRepository userRepository;
    @Resource
    JwtUtil jwtUtil;
    @Resource
    OpreationLogRepository opreationLogRepository;
    private final Boolean isDebug = true;
    private Object[] args;

    @Pointcut("execution(* club.web51.classroominteractiontoolsadmin.Controllers..*.*(..))")
    public void doHandler(){

    }

    @Around("doHandler()")
    public Object exception(ProceedingJoinPoint joinPoint){
        try{
            if(isDebug)
                outLog(joinPoint);
            //校验
            args = joinPoint.getArgs();
            beforePoint(joinPoint);
            //放行
            Object result = joinPoint.proceed(args);
            if(isDebug)
                log.info("\n[结果] "+result);
            return result;
        } catch (Exception e) {
            e.printStackTrace();
            if(e instanceof ErrDataException){
                return MsgUtil.fail(e.getMessage());
            }else{
                return MsgUtil.fail();
            }
        } catch (Throwable e) {
            return MsgUtil.fail("系统异常");
        }
    }

    /**
     * 输出调试信息
     * @param joinPoint
     */
    @Async
    public void outLog(ProceedingJoinPoint joinPoint){
        RequestAttributes ra = RequestContextHolder.getRequestAttributes();
        ServletRequestAttributes sra = (ServletRequestAttributes) ra;
        HttpServletRequest request = sra.getRequest();
        String token = request.getHeader("X-Token");
        Long uid = 0L;
        if(Objects.nonNull(token) && !token.equals(""))
        uid = jwtUtil.getUid(token);
        Api a = (Api) joinPoint.getSignature().getDeclaringType().getAnnotation(Api.class);
        String clazzName = a.tags()[0];
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        ApiOperation apiOperation = methodSignature.getMethod().getAnnotation(ApiOperation.class);
        String methodName = "";
        if(Objects.nonNull(apiOperation))
        methodName = apiOperation.value();
        Object[] objects = joinPoint.getArgs();
        if(Objects.isNull(clazzName)){
            clazzName = joinPoint.getSignature().getDeclaringType().getName();
        }
        if(methodName.equals("")){
            methodName = joinPoint.getSignature().getName();
        }
        log.info("\n[动作] [用户"+uid+"]执行[" + clazzName + "]中的[" + methodName + "]\n[参数] "+ Arrays.toString(objects));
        Integer type = 1;
        if(methodName.contains("修改") || methodName.contains("增加") || methodName.contains("更新") ||methodName.contains("创建")||methodName.contains("添加")){
            type = 2;
        }
        OpreationLog op = OpreationLog.builder().uid(uid).type(type).description("执行了["+clazzName+"]中的["+methodName+"]").build();
        opreationLogRepository.save(op);
    }

    private Boolean beforePoint(ProceedingJoinPoint joinPoint){
        RequestAttributes ra = RequestContextHolder.getRequestAttributes();
        ServletRequestAttributes sra = (ServletRequestAttributes) ra;
        HttpServletRequest request = sra.getRequest();
        String token = request.getHeader("X-Token");
        //得到要进入的是哪个controller方法
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();

        Method method = signature.getMethod();
        Class clazz = signature.getMethod().getDeclaringClass();

        if(method.isAnnotationPresent(NonRequireToken.class) || clazz.isAnnotationPresent(NonRequireToken.class)){
            NonRequireToken nonRequireToken = (NonRequireToken) ObjectUtil.NonNullDefault(method.getAnnotation(NonRequireToken.class),clazz.getDeclaredAnnotation(NonRequireToken.class));
            if(nonRequireToken.required()){
                return true;
            }
        }

        if(method.isAnnotationPresent(RequireToken.class) || clazz.isAnnotationPresent(RequireToken.class)){
            RequireToken requireToken = (RequireToken) ObjectUtil.NonNullDefault(method.getAnnotation(RequireToken.class),clazz.getAnnotation(RequireToken.class));
            if(requireToken.required()){
                if(Objects.isNull(token) || "".equals(token)){
                    throw new ErrDataException(MsgEnum.ERROR_USER_DATA_INVAILD);
                }
                if(!jwtUtil.verify(token)){
                    throw new ErrDataException(MsgEnum.ERROR_USER_DATA_INVAILD);
                }
                Long uid = jwtUtil.getUid(token);

                TokenVO tokenVO = TokenVO.builder().token(token).uid(uid).build();

                Object[] objects = joinPoint.getArgs();
                for (int i = 0; i < objects.length; i++) {
                    if(objects[i] instanceof TokenVO){
                        objects[i] = tokenVO;
                    }
                }
                args = objects;
//                List<Object> objects1 = Arrays.stream(objects).collect(Collectors.toList());
//                objects1.add(tokenVO);
//                objects = objects1.toArray();
//                System.out.println("u2"+ Arrays.toString(objects));
//                joinPoint.

            }
        }
        return true;
    }
}
